package com.mai4j.maiadmin.configure;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.jfinal.template.Engine;
import com.jfinal.template.ext.spring.JFinalViewResolver;
import com.jfinal.template.source.FileSourceFactory;
import com.mai4j.maiadmin.support.i18n.DefaultLocale;
import com.mai4j.maiadmin.support.i18n.I18n;
import com.mai4j.maiadmin.support.i18n.I18nLocaleResolver;
import com.mai4j.maiadmin.support.security.SecurityUtils;
import com.mai4j.maiadmin.support.web.ClientInterceptor;
import com.mai4j.maiadmin.support.web.binding.ReqParamArgumentResolver;
import org.apache.commons.io.FilenameUtils;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.util.ResourceUtils;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

import java.util.List;
import java.util.stream.Collectors;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * Json objectMapper
     */
    private final ObjectMapper objectMapper;
    /**
     * maiProperties
     */
    private final MaiProperties maiProperties;
    /**
     * 默认的Locale提供者
     */
    private final DefaultLocale defaultLocale;

    static final String RES_PATH = "/resources/**";

    public WebMvcConfig(ObjectMapper objectMapper, MaiProperties maiProperties, DefaultLocale defaultLocale) {
        this.objectMapper = objectMapper;
        this.maiProperties = maiProperties;
        this.defaultLocale = defaultLocale;
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new I18nLocaleResolver(defaultLocale);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(RES_PATH)
                .addResourceLocations(ResourceUtils.FILE_URL_PREFIX + maiProperties.getResourcePath(),
                        ResourceUtils.FILE_URL_PREFIX + maiProperties.getImagePath());
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor()); // 通过URL改变Locale
        registry.addInterceptor(new ClientInterceptor());       // 客户端信息拦截
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new ReqParamArgumentResolver(objectMapper));
    }

    /**
     * 配置模板引擎
     *
     * @param serverProperties serverProperties
     * @param i18n             i18n
     * @return JFinalViewResolver
     */
    @Bean
    public JFinalViewResolver getJFinalViewResolver(ServerProperties serverProperties, I18n i18n) {
        // 创建用于整合 spring boot 的 ViewResolver 扩展对象
        JFinalViewResolver jfr = new JFinalViewResolver();
        // 对 spring boot 进行配置
        jfr.setCreateSession(false); // 禁用session
        jfr.setSuffix(".vue");
        jfr.setContentType("text/html;charset=UTF-8");
        jfr.setOrder(0);
        // 设置在模板中可通过 #(session.value) 访问 session 中的数据
        jfr.setSessionInView(true);
        // 获取 engine 对象，对 enjoy 模板引擎进行配置，配置方式与前面章节完全一样
        Engine engine = JFinalViewResolver.engine;
        // 热加载配置能对后续配置产生影响，需要放在最前面
        engine.setDevMode(maiProperties.getTemplate().isHot());
        // 使用 FileSourceFactory 从 文件系统中加载模板文件
        engine.setSourceFactory(new FileSourceFactory());
        engine.setBaseTemplatePath(maiProperties.getTemplate().getBasePath());
        // 添加常量
        engine.addSharedObject("TIMESTAMP", System.currentTimeMillis()); // 启动时间戳，用于作为静态资源后缀，防止缓存
        engine.addSharedObject("GATEWAY", maiProperties.getGateway());  // 网关的域名
        engine.addSharedObject("CONTEXT_PATH", serverProperties.getServlet().getContextPath());
        engine.addSharedObject("i18n", i18n);
        engine.addSharedStaticMethod(SecurityUtils.class);
        engine.addSharedStaticMethod(SecurityUtils.Helper.class); // 添加权限相关指令
        return jfr;
    }

    /**
     * 设置Validator读取的文件路径
     *
     * @param messageSource messageSource
     * @return LocalValidatorFactoryBean
     */
    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) {
        LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
        validatorFactoryBean.setValidationMessageSource(messageSource);
        return validatorFactoryBean;
    }

    /**
     * I18n文件源
     *
     * @param maiProperties maiProperties
     * @return MessageSource
     */
    @Bean
    public MessageSource messageSource(MaiProperties maiProperties) {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        MaiProperties.I18n i18nProperties = maiProperties.getI18n();
        List<String> fileBundles = i18nProperties.getFileBundles();
        List<String> classpathBundles = i18nProperties.getClasspathBundles();
        List<String> bundles = fileBundles.stream().map(b -> FilenameUtils.normalize(i18nProperties.getPath()) + b).collect(Collectors.toList());
        bundles.addAll(classpathBundles.stream().map(b -> "classpath:" + b).collect(Collectors.toList()));
        messageSource.setBasenames(bundles.toArray(new String[]{}));
        messageSource.setDefaultEncoding(i18nProperties.getEncoding());
        messageSource.setCacheMillis(i18nProperties.getCacheDuration().toMillis());
        return messageSource;
    }
}